﻿namespace NetFx.SMIL
{
	using System;
	using System.Collections.Generic;
	using System.Text;
	using System.Reflection;
	using System.Reflection.Emit;
	using NetFx.AGIL;
  using NetFx.SMIL.Definitions;
  using NetFx.SMIL.References;

  /// <summary>
  /// 堆栈关系。
  /// </summary>
  public class StackRelation
  {
    #region Statics
    public static StackRelation of(SmilOperation operation, Type stackType)
    {
      return new StackRelation(operation, stackType);
    }
    #endregion
    #region Fields
    protected SmilOperation operation = null;
    protected Type stackType = null;
    #endregion
    #region Properties
    public virtual SmilOperation Operation
    {
      get
      {
        return this.operation;
      }
    }
    public virtual Type StackType
    {
      get
      {
        return this.stackType;
      }
    }
    #endregion
    #region Constructors
    public StackRelation(SmilOperation operation, Type stackType)
    {
      this.operation = operation;
      this.stackType = stackType;
    }
    #endregion
  }

	/// <summary>
	/// SmilCode 代码分析器。
	/// </summary>
	public class SmilCodeAnalyser : IDisposable
  {
    #region Statics
    public static SmilCodeAnalyser of(MethodBase method)
    {
      return new SmilCodeAnalyser(method);
    }
    public static SmilCodeAnalyser of(MethodDef method)
    {
      return new SmilCodeAnalyser(method);
    }
    #endregion
    #region Fields
    /// <summary>
		/// 需分析的方法定义。
		/// </summary>
    protected MethodDef method = null;
		/// <summary>
		/// Agil阅读器。
		/// </summary>
    protected AgilGeneratorReader reader = null;
    /// <summary>
    /// 记录是否已经处置。
    /// </summary>
    protected bool isDisposed = false;

    protected Stack<StackRelation> stack = null;
		#endregion
		#region Properties
    /// <summary>
    /// 获取需要分析的方法定义。
    /// </summary>
    public virtual MethodDef Method
    {
      get
      {
        return this.method;
      }
    }
    public virtual List<LabelDef> Labels
    {
      get
      {
        return this.Method.Labels;
      }
    }
    public virtual AgilGeneratorReader Reader
    {
      get
      {
        return this.reader;
      }
    }
    public virtual bool IsDisposed
    {
      get
      {
        return this.isDisposed;
      }
    }
    /// <summary>
    /// 获取关系栈
    /// </summary>
    protected virtual Stack<StackRelation> Stack
    {
      get
      {
        return this.stack == null ?
          this.stack = new Stack<StackRelation>() :
          this.stack;
      }
    }
		#endregion
		#region Constructors
    public SmilCodeAnalyser(MethodBase method)
      : this(MethodDef.of(method,false))
    {
    }
    public SmilCodeAnalyser(MethodDef method)
    {
      if ((this.method = method) == null) throw new ArgumentNullException("method can not be null!");

      if ((this.reader = AgilGeneratorReader.of(method.Base)) == null)
      {
        throw new InvalidOperationException("can not initialize reader!");
      }

    }
		~SmilCodeAnalyser()
		{
			this.Dispose();
		}
		#endregion
		#region Methods
    #region Parsing
    #region Smil Parsing
    public virtual bool Parse(List<SmilOperation> operations)
    {
      int index = 0;

      return this.Parse(operations, ref index);
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    /// <remarks>
    /// 注意，此方法将更新方法定义中的标签列表（清除原来的标签定义）。
    /// </remarks>
		public virtual bool Parse(List<SmilOperation> operations, ref int index)
		{
      if (operations != null)
      {
        this.Labels.Clear();

        List<AgilOperation> agils = this.FixLabels(this.LowParse());

        for(index = 0;index<agils.Count;index++)
        {
          AgilOperation agil = agils[index];

          if (agil != null)
          {
            //查询 AgilOperation 对应的 SmilOpcode。
            SmilOperation op = SmilOperation.GetTemplateOf(agil);

            if (op != null)
            {
              operations.Add(this.ParseStack(op,this.Stack));
            }
            else
            {
              return false;
            }
          }
        }

        this.ComposeLabelOperations(operations);

      }
      //the stack should be empty (the last item is eaten by ret).
      return this.CheckStack(this.Stack);
    }

    protected virtual bool CheckStack(Stack<StackRelation> stack)
    {
      return stack != null && stack.Count == 0;
    }

    protected virtual bool IsVarientLength(Type[] stacks)
    {
      return (stacks != null && stacks.Length == 1 && stacks[0] == typeof(System.Array));
    }

    protected virtual bool IsZeroLength(Type[] stacks)
    {
      return (stacks != null && stacks.Length == 0);
    }

    protected virtual SmilOperation DumpStack(SmilOperation operation, Stack<StackRelation> stack)
    {

      StackRelation relation = stack != null ? stack.Pop() : null;

      if (operation!=null && relation != null)
      {
        operation.Forwards.Add(relation.Operation);
      }

      return operation;
    }

    protected virtual SmilOperation FillStack(SmilOperation operation,Type type, Stack<StackRelation> stack)
    {
      if (stack != null && operation!=null && type!=null)
      {
        stack.Push(StackRelation.of(operation, type));
      }
      return operation;
    }

    protected virtual SmilOperation ParsePop(SmilOperation operation, Stack<StackRelation> stack)
    {
      if (operation != null && stack != null)
      {
        Type[] popStacks = operation.PopStackTypes;

        if (popStacks != null && popStacks.Length > 0 && stack.Count > 0)
        {
          if (this.IsVarientLength(popStacks)) //for call/calli/callvirt
          {
            SmilOperand operand = operation.Operand;

            if (operand != null)
            {
              MethodRef method = operand.ValueRef as MethodRef;

              if (method != null)
              {
                for (int i = 0; i < method.ParametersCount; i++)
                {
                  this.DumpStack(operation, stack);
                }
              }
              else //should be ret
              {
                MethodDef methodDef = this.method;

                if (methodDef != null)
                {
                  TypeRef typeRef = methodDef.ReturnType;

                  if (typeRef != null)
                  {
                    Type type = typeRef.ReflectInfo;

                    if (type != null && type != typeof(void))
                    {
                      this.DumpStack(operation, stack);
                    }
                  }
                }
              }
            }
          }
          else
          {
            foreach (Type type in popStacks)
            {
              //NOTICE: here does not check the validation
              //if (this.IsValid(this.Stack.Peek(), type)){}
              this.DumpStack(operation, stack);
            }
          }
        }
      }
      return operation;
    }

    protected virtual SmilOperation ParsePush(SmilOperation operation, Stack<StackRelation> stack)
    {
      if (operation != null && stack != null)
      {
        Type[] pushStacks = operation.PushStackTypes;

        if (pushStacks != null && pushStacks.Length > 0)
        {
          if (this.IsVarientLength(pushStacks))
          {
            SmilOperand operand = operation.Operand;

            if (operand != null)
            {
              MethodRef method = operand.ValueRef as MethodRef;

              if (method != null)
              {
                Type returnType = method.ReturnType;

                if (returnType != null && returnType != typeof(void))
                {
                  this.FillStack(operation, returnType,stack);
                }
              }
            }
          }
          else
          {
            foreach (Type type in pushStacks)
            {
              this.FillStack(operation, type,stack);
            }
          }
        }
      }
      return operation;
    }

    protected virtual SmilOperation ParseStack(SmilOperation operation, Stack<StackRelation> stack)
    {
      if (operation != null && stack!=null)
      {
        //Only leave/leave_s can clear stack
        if (operation.Matches(SmilOperation.GoFor))
        {
          this.Stack.Clear();
        }
        else
        {
          this.ParsePop(operation, stack);

          this.ParsePush(operation, stack);
        }

        //Tag the root operation, which can be further extract to make statements.
        operation.IsRoot = (stack.Count == 0);
      }
      return operation;
    }

    #endregion
    #region Agil Parsing
    protected virtual List<AgilOperation> LowParse()
    {
      List<AgilOperation> operations = new List<AgilOperation>();
      if (this.reader != null)
      {
        if (!this.reader.SymanticParse(operations,false))
        {
          operations = null;
        }
      }
      return operations;
    }
    #endregion
    #endregion
    #region Lables
    protected virtual void ComposeLabelOperations(List<SmilOperation> operations)
    {
      if (operations != null)
      {
        foreach (LabelDef label in this.Labels)
        {
          if (label != null)
          {
            SmilOperation labelOperation = SmilOperation.Mark.Clone();

            if (labelOperation != null)
            {
              labelOperation.SetOperand(SmilOperand.of(LabelRef.of(label.Name)));

              this.InsertOperation(operations, label.Address, labelOperation);
            }
          }
        }
      }
    }

    protected virtual bool InsertOperation(List<SmilOperation> list, long position, SmilOperation insertSmil)
    {
      return InsertOperation(list, position, insertSmil, true);
    }

    protected virtual bool InsertOperation(List<SmilOperation> list, long position, SmilOperation insertSmil, bool singleton)
    {
      if (list != null)
      {
        int index = this.FindPosition(list, position);
        if (index >= 0)
        {
          SmilOperation smil = list[index];

          if (smil != null)
          {
            if (smil.Matches(insertSmil))
            {
              if (!singleton)
              {
                insertSmil.Position = position;
                //Prepose
                list.Insert(index, insertSmil);
                return true;
              }
            }
            else
            {
              insertSmil.Position = position;
              //Prepose
              list.Insert(index, insertSmil);
              return true;
            }
          }
        }
      }
      return false;
    }

    protected virtual int FindPosition(List<SmilOperation> list, long position)
    {
      if (list != null)
      {
        for (int i = 0; i < list.Count; i++)
        {
          SmilOperation smil = list[i];

          if (smil != null && smil.Position == position)
          {
            return i;
          }
        }
      }
      return -1;
    }


    
    protected virtual bool IsBranchingAgil(AgilOperation agil)
    {
      return agil != null ? (
        (agil.OperandType == MsilOperandType.InlineBrTarget || agil.OperandType == MsilOperandType.ShortInlineBrTarget || agil.OperandType == MsilOperandType.InlineSwitch)
        &&
        (agil.FlowControl == MsilFlowControl.Branch || agil.FlowControl == MsilFlowControl.Cond_Branch)) : false;
    }
    protected virtual List<AgilOperation> FixLabels(List<AgilOperation> agils)
    {
      return this.FixLabels(agils, this.Labels);
    }
    protected virtual List<AgilOperation> FixLabels(List<AgilOperation> agils, List<LabelDef> labels)
    {
      if (agils != null && labels != null)
      {
        foreach (AgilOperation agil in agils)
        {
          if (agil != null && this.IsBranchingAgil(agil))
          {
            long tailAddress = agil.NextOperationOffset;

            if (agil.OperandType == MsilOperandType.InlineSwitch)
            {
              if (agil.IsOpcodeMatched(AgilOperation.Switch) && agil.HasSwitchesOperand)
              {
                int[] switches = agil.SwitchesOperand;

                foreach (int address in switches)
                {
                  agil.Labels +=
                    this.FixLabelAt(tailAddress, address, labels).Name + AgilOperation.LabelSeparator;
                }
              }
            }
            else
            {
              agil.Labels = this.FixLabelAt(tailAddress, agil.AddressOperand, labels).Name;
            }

          }
        }
      }
      return agils;
    }
    protected virtual LabelDef FixLabelAt(long tailAddress, int address, List<LabelDef> labels)
    {
      LabelDef label = null;
      if (labels != null)
      {
        long pos = tailAddress + address;
        for(int i = 0;i<labels.Count;i++)
        {
          label = labels[i];
          if (label.Address == pos)
            return label;
        }
        label = LabelDef.of(pos);
        labels.Add(label);
      }
      return label;
    }
    #endregion
    #region Local Naming
    protected virtual string NameLocalVariable(int index, IList<LocalVariableInfo> locals)
    {
      if (locals != null && index >= 0 && index < locals.Count)
      {
        LocalVariableInfo local = locals[index];
        if (local != null && local.LocalType != null)
        {
          return string.Format("{0}{1}",
            this.NameLocalVariablePrefix(local.LocalType),
            this.GetOrderIndex(local.LocalType, local.LocalIndex, locals)
            );
        }
      }
      return string.Empty;
    }
    protected virtual string NameLocalVariablePrefix(Type type)
    {
      string typeNameLiteral = string.Empty;

      if (type != null)
      {
        typeNameLiteral = this.RenameType(type);

        if (!string.IsNullOrEmpty(typeNameLiteral))
        {
          if (char.IsUpper(typeNameLiteral, 0))
          {
            char[] chs = typeNameLiteral.ToCharArray();

            chs[0] = char.ToLower(chs[0]);

            typeNameLiteral = new string(chs);
          }
          else
          {
            typeNameLiteral = "_" + typeNameLiteral;
          }
        }
      }

      return typeNameLiteral;
    }
    protected virtual string RenameType(Type type)
    {
      string name = string.Empty;
      if (type != null)
      {
        TypeCode typeCode = Type.GetTypeCode(type);
        switch (typeCode)
        {
          case TypeCode.Boolean:
            name = "bool";
            break;
          case TypeCode.Byte:
            name = "byte";
            break;
          case TypeCode.Char:
            name = "char";
            break;
          case TypeCode.DateTime:
            name = "date";
            break;
          case TypeCode.Decimal:
            name = "decimal";
            break;
          case TypeCode.Double:
            name = "double";
            break;
          case TypeCode.Int16:
            name = "short";
            break;
          case TypeCode.Int32:
            name = "int";
            break;
          case TypeCode.Int64:
            name = "long";
            break;
          case TypeCode.Object:
            name = type.Name;
            break;
          case TypeCode.SByte:
            name = "sbyte";
            break;
          case TypeCode.Single:
            name = "float";
            break;
          case TypeCode.String:
            name = "string";
            break;
          case TypeCode.UInt16:
            name = "ushort";
            break;
          case TypeCode.UInt32:
            name = "uint";
            break;
          case TypeCode.UInt64:
            name = "ulong";
            break;
          default:
            name = type.Name;
            break;
        }
      }
      return name;
    }
    protected virtual int GetOrderIndex(Type type, int destIndex, IList<LocalVariableInfo> locals)
    {
      int orderIndex = 1;
      if (locals != null && locals.Count > 0)
      {
        for (int i = 0; i < destIndex; i++)
        {
          LocalVariableInfo local = locals[i];
          if (local != null && type == local.LocalType)
          {
            orderIndex++;
          }
        }
      }
      return orderIndex;
    }
    #endregion
    #region Utils
    public virtual void Dispose()
		{
			this.Dispose(!this.IsDisposed);
		}
		protected virtual void Dispose(bool disposing)
		{
      if (disposing)
			{
        if (this.reader != null)
        {
          if (!this.reader.IsDisposed)
          {
            this.reader.Dispose();
          }
          this.reader = null;
        }
        this.isDisposed = true;
			}
		}
		#endregion
		#endregion
	}
}
